package com.xbongbong.saas.service.toolbox.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.LinkInfoPojo;
import com.xbongbong.paas.field.MultiUnitItemPoJo;
import com.xbongbong.paas.field.SubFormPoJo;
import com.xbongbong.paas.field.UnitItemPoJo;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.pojo.ProductFieldPojo;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.PageHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainInLinkItemDTO;
import com.xbongbong.pro.service.toolbox.statistic.help.ChartQueryHelp;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.statistic.pojo.vo.ProductInfoVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.MultiUnitConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.ProductConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.BomProductEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.ProductCategoryEntity;
import com.xbongbong.saas.domain.entity.ProductSerialBalanceEntity;
import com.xbongbong.saas.domain.entity.ProductionOrderProductEntity;
import com.xbongbong.saas.domain.entity.PromotionProductEntity;
import com.xbongbong.saas.domain.entity.PurchaseProductEntity;
import com.xbongbong.saas.domain.entity.ext.CustomerEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.WarehouseEntityExt;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.LinkProductSelectSerialEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.AssembleEnum;
import com.xbongbong.saas.enums.business.DistributorEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.MarketingBaseEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.PromotionActivityManagementEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.business.SerialEnum;
import com.xbongbong.saas.enums.business.SerialStatusEnum;
import com.xbongbong.saas.enums.business.TransferEnum;
import com.xbongbong.saas.enums.business.WarehouseEnum;
import com.xbongbong.saas.enums.dictionary.ApplicableObjectTypeEnum;
import com.xbongbong.saas.enums.dictionary.MarketingStatusEnum;
import com.xbongbong.saas.enums.product.InstockProductEnum;
import com.xbongbong.saas.enums.product.PriceProductEnum;
import com.xbongbong.saas.enums.product.PriceSetEnum;
import com.xbongbong.saas.help.SaasFormHelp;
import com.xbongbong.saas.model.BomProductModel;
import com.xbongbong.saas.model.CustomerModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.ProductCategoryModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.ProductSerialBalanceModel;
import com.xbongbong.saas.model.ProductSerialFlowModel;
import com.xbongbong.saas.model.ProductionOrderProductModel;
import com.xbongbong.saas.model.PromotionProductModel;
import com.xbongbong.saas.model.PurchaseProductModel;
import com.xbongbong.saas.model.SpecificationModel;
import com.xbongbong.saas.model.WarehouseModel;
import com.xbongbong.saas.service.toolbox.handle.WebCommonDataValueMatchHandle;
import com.xbongbong.saas.service.toolbox.handle.WebListDataValueMatchHandle;
import com.xbongbong.sys.model.PriceProductSetModel;
import com.xbongbong.sys.model.PriceSetModel;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static com.xbongbong.paas.elasticsearch.constant.EsNormalConstant.MINI_MUM_SHOULD_MATCH;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 产品工具类，内部包含：产品、入库产品、出库产品、仓库等
 * @author zcp
 * @version v1.0
 * @date 2019/4/9 10:35
 * @since v1.0
 */
@Component
public class ProductHelp {
    @Resource
    private EsHelper esHelper;
    @Resource
    private ChartQueryHelp chartQueryHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private ProductModel productModel;
    @Resource
    private ProductCategoryModel productCategoryModel;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private PurchaseProductModel purchaseProductModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private SpecificationModel specificationModel;
    @Resource
    private BomProductModel bomProductModel;
    @Resource
    private ProductionOrderProductModel productionOrderProductModel;
    @Resource
    private ProductSerialBalanceModel productSerialBalanceModel;
    @Resource
    private WarehouseModel warehouseModel;
    @Resource
    private SaasFormHelp saasFormHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PriceProductSetModel priceProductSetModel;
    @Resource
    private PriceSetModel priceSetModel;
    @Resource
    private CustomerModel customerModel;
    @Resource
    private WebCommonDataValueMatchHandle webCommonDataValueMatchHandle;
    @Resource
    private WebListDataValueMatchHandle webListDataValueMatchHandle;
    @Resource
    private PromotionProductModel promotionProductModel;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private ProductSerialFlowModel productSerialFlowModel;

    /**
     * 处理产品分类筛选：当查询全部分类，无需筛选分类id；当筛选某个分类，则获取所有分类及其子分类下的产品
     * 如果全部分类，则不会对categoryIdNameMap处理
     * 如果某个分类，则会构建分类及其子分类的id和名称的对应关系置入categoryIdNameMap
     * @param corpid 公司id
     * @param productQueryBuilder 查询产品条件
     * @param categoryId 筛选分类id
     * @return java.util.Map<java.lang.Long,java.lang.String>
     * @author zcp
     * @date 2019/4/10 20:51
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, String> setCategoryQuery(String corpid, BoolQueryBuilder productQueryBuilder, Long categoryId) {
        Map<Long, String> categoryIdNameMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //全部分类的值
        Long allCategory = 0L;
        if (categoryId != null && !allCategory.equals(categoryId)) {
            //产品分类ids
            Set<Long> categoryIdIn = new HashSet<>();
            categoryIdIn.add(-1L);
            List<ProductCategoryEntity> productCategoryList = formHelp.getProductCategoryListById(corpid, categoryId);
            for (ProductCategoryEntity entity : productCategoryList) {
                categoryIdIn.add(entity.getId());
                categoryIdNameMap.put(entity.getId(), entity.getName());
            }
            productQueryBuilder.filter(termsQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.CATEGORY_ID), categoryIdIn));
        }
        return categoryIdNameMap;
    }
    //-------------------------------获取入库相关-------------------------------
    /**
     * （从saas移入）处理入库单内关联产品信息（产品更新时，不会更新入库单内的产品编号等信息）
     * 注，产品从es查询，内部处理好产品编号、产品分类、产品封面图、产品条码、产品规格等信息
     * @param instockIdIn 入库单id数组
     * @param corpid 公司id
     * @return java.util.List<com.xbongbong.saas.domain.entity.OutstockProductEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/8 19:52
     * @since v1.0
     * @version v1.0
     */
    public List<InstockProductEntity> getInstockProduct(Collection<Long> instockIdIn, String corpid) throws XbbException {
        if (instockIdIn == null || instockIdIn.size() == 0) {
            return new ArrayList<>();
        }
        Map<String, Object> param = new HashMap<>(3);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("instockIdIn", instockIdIn);
        List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
        Set<Long> productIdIn = new HashSet<>();
        Set<Long> houseIdIn = new HashSet<>();
        for (InstockProductEntity entity : instockProductEntityList) {
            productIdIn.add(entity.getProductId());
            houseIdIn.add(entity.getWarehouseId());
        }
        List<PaasFormDataEntityExt> warehouseList = getWarehouseListByIdIn(corpid, houseIdIn, null);
        String warehouseNameAttr = WarehouseEnum.WAREHOUSE_NAME.getAttr();
        //仓库id--仓库实体
        Map<Long, PaasFormDataEntityExt> idWarehouseMap = formHelp.getMap4IdEntity(warehouseList);
        //产品list
        List<ProductInfoVO> productList = productModel.getProductListByIdInEs(corpid, productIdIn, null);
        Set<Long> categoryIdIn = new HashSet<>();
        String categoryIdAttr = ProductEnum.CATEGORY_ID.getAttr();
        for (ProductInfoVO product : productList) {
            JSONObject data = product.getData();
            if (data == null) {
                continue;
            }
            categoryIdIn.add(data.getLong(categoryIdAttr));
        }
        //产品id--产品信息（内部调用es方式实现）
        Map<Long, ProductInfoVO> productMap = productModel.getProductMapByIdIn4Es(productList);
        /**
         产品id--父产品信息
         国军：产品编号要用父级产品的编号（子级产品的编号是在父级产品编号上进行自增的）
         */
        Map<Long, ProductInfoVO> childParentMap = productModel.getParentProductListByIdIn4Es(corpid, productList, null);
        //分类id--分类信息
        Map<Long, ProductCategoryEntity> categoryMap = productCategoryModel.getProductCategoryMapByIdIn(corpid, categoryIdIn);
        String productNoAttr = ProductEnum.PRODUCT_NO.getAttr();
        String nameAttr = ProductEnum.NAME.getAttr();
        String unitAttr = ProductEnum.UNIT.getAttr();
        //获取启用的产品单位
        Map<String, String> unitMap = formHelp.getProductUnitItemMap(corpid);
        Map<String, String> businessUnitItemMap = formHelp.getBusinessUnitItemMap(corpid);
        String thumbnailAttr = ProductEnum.THUMBNAIL.getAttr();
        String barcodeAttr = ProductEnum.BARCODE.getAttr();
        String specificationAttr = ProductEnum.SPECIFICATION_LINK_TEXT.getAttr();
        for (InstockProductEntity entity : instockProductEntityList) {
            Long productId = entity.getProductId();
            ProductInfoVO parent = childParentMap.get(productId);
            if (parent != null) {
                JSONObject data = JsonHelperUtil.getJSONObject(parent.getData());
                entity.setProductNo(data.getString(productNoAttr));
            }
            Long warehouseId = entity.getWarehouseId();
            PaasFormDataEntityExt warehouseEsEntity = idWarehouseMap.get(warehouseId);
            if (warehouseEsEntity != null) {
                JSONObject data = JsonHelperUtil.getJSONObject(warehouseEsEntity.getData());
                entity.setWarehouseName(data.getString(warehouseNameAttr));
            }
            ProductInfoVO product = productMap.get(productId);
            String unit;
            String specification;
            if (product != null) {
                JSONObject data = product.getData();
                if (data == null) {
                    continue;
                }
                unit = unitMap.getOrDefault(data.getString(unitAttr), "");
                specification = specificationModel.joinSpecification(data.getString(specificationAttr));
                entity.setProductName(data.getString(nameAttr));
                entity.setImageUrl(data.getString(thumbnailAttr));
                entity.setBarcode(data.getString(barcodeAttr));
                //分类
                Long categoryId = data.getLongValue(categoryIdAttr);
                ProductCategoryEntity category = categoryMap.get(categoryId);
                String categoryName = (category == null) ? "" : category.getName();
                entity.setCategoryId(categoryId);
                entity.setCategoryName(categoryName);
            } else {
                //产品不存在，则读取入库产品表本身存放的产品相关信息并进行处理
                unit = businessUnitItemMap.getOrDefault(entity.getProductUnit(), "");
                specification = specificationModel.joinSpecification(entity.getProductSpecification());
            }
            entity.setProductUnit(unit);
            entity.setProductSpecification(specification);
        }
        return instockProductEntityList;
    }

    //-------------------------------获取出库相关-------------------------------
    /**
     * （从saas移入）处理出库单内关联产品信息（产品更新时，不会更新出库单内的产品编号等信息）
     * 注，产品从es查询，内部处理好产品编号、产品分类、产品封面图、产品条码、产品规格等信息
     * @param outstockIdIn 出库单id数组
     * @param corpid 公司id
     * @return java.util.List<com.xbongbong.saas.domain.entity.OutstockProductEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/8 19:52
     * @since v1.0
     * @version v1.0
     */
    public List<OutstockProductEntity> getOutstockProduct(Collection<Long> outstockIdIn, String corpid) throws XbbException {
        if (outstockIdIn == null || outstockIdIn.size() == 0) {
            return new ArrayList<>();
        }
        Map<String, Object> param = new HashMap<>(3);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("outstockIdIn", outstockIdIn);
        List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
        Set<Long> productIdIn = new HashSet<>();
        Set<Long> houseIdIn = new HashSet<>();
        for (OutstockProductEntity entity : outstockProductEntityList) {
            productIdIn.add(entity.getProductId());
            houseIdIn.add(entity.getWarehouseId());
        }
        //仓库list
        List<PaasFormDataEntityExt> warehouseList = getWarehouseListByIdIn(corpid, houseIdIn, null);
        String warehouseNameAttr = WarehouseEnum.WAREHOUSE_NAME.getAttr();
        //仓库id--仓库实体
        Map<Long, PaasFormDataEntityExt> idWarehouseMap = formHelp.getMap4IdEntity(warehouseList);
        //产品list
        List<ProductInfoVO> productList = productModel.getProductListByIdInEs(corpid, productIdIn, null);
        Set<Long> categoryIdIn = new HashSet<>();
        String categoryIdAttr = ProductEnum.CATEGORY_ID.getAttr();
        for (ProductInfoVO product : productList) {
            JSONObject data = product.getData();
            if (data == null) {
                continue;
            }
            categoryIdIn.add(data.getLong(categoryIdAttr));
        }
        //产品id--产品信息（内部调用es方式实现）
        Map<Long, ProductInfoVO> productMap = productModel.getProductMapByIdIn4Es(productList);
        /**
         产品id--父产品信息
         国军：产品编号要用父级产品的编号（子级产品的编号是在父级产品编号上进行自增的）
         */
        Map<Long, ProductInfoVO> childParentMap = productModel.getParentProductListByIdIn4Es(corpid, productList, null);
        //分类id--分类信息
        Map<Long, ProductCategoryEntity> categoryMap = productCategoryModel.getProductCategoryMapByIdIn(corpid, categoryIdIn);
        String productNoAttr = ProductEnum.PRODUCT_NO.getAttr();
        String nameAttr = ProductEnum.NAME.getAttr();
        String unitAttr = ProductEnum.UNIT.getAttr();
        //获取启用的产品单位
        Map<String, String> unitMap = formHelp.getProductUnitItemMap(corpid);
        Map<String, String> businessUnitItemMap = formHelp.getBusinessUnitItemMap(corpid);
        String thumbnailAttr = ProductEnum.THUMBNAIL.getAttr();
        String barcodeAttr = ProductEnum.BARCODE.getAttr();
        String specificationAttr = ProductEnum.SPECIFICATION_LINK_TEXT.getAttr();
        for (OutstockProductEntity entity : outstockProductEntityList) {
            Long productId = entity.getProductId();
            ProductInfoVO parent = childParentMap.get(productId);
            if (parent != null) {
                JSONObject data = JsonHelperUtil.getJSONObject(parent.getData());
                entity.setProductNo(data.getString(productNoAttr));
            }
            Long warehouseId = entity.getWarehouseId();
            PaasFormDataEntityExt warehouseEsEntity = idWarehouseMap.get(warehouseId);
            if (warehouseEsEntity != null) {
                JSONObject data = JsonHelperUtil.getJSONObject(warehouseEsEntity.getData());
                entity.setWarehouseName(data.getString(warehouseNameAttr));
            }
            ProductInfoVO product = productMap.get(productId);
            String unit;
            String specification;
            if (product != null) {
                JSONObject data = product.getData();
                if (data == null) {
                    continue;
                }
                unit = unitMap.getOrDefault(data.getString(unitAttr), "");
                specification = specificationModel.joinSpecification(data.getString(specificationAttr));
                entity.setProductName(data.getString(nameAttr));
                entity.setImageUrl(data.getString(thumbnailAttr));
                entity.setBarcode(data.getString(barcodeAttr));
                //分类
                Long categoryId = data.getLongValue(categoryIdAttr);
                ProductCategoryEntity category = categoryMap.get(categoryId);
                String categoryName = (category == null) ? "" : category.getName();
                entity.setCategoryId(categoryId);
                entity.setCategoryName(categoryName);
            } else {
                //产品不存在，则读取出库产品表本身存放的产品相关信息并进行处理
                unit = businessUnitItemMap.getOrDefault(entity.getProductUnit(), "");
                specification = specificationModel.joinSpecification(entity.getProductSpecification());
            }
            entity.setProductUnit(unit);
            entity.setProductSpecification(specification);
        }
        return outstockProductEntityList;
    }

    //-------------------------------获取仓库相关-------------------------------
    /**
     * 通过id列表获取仓库
     *
     * @param corpid    公司id
     * @param houseIdIn 产品id列表
     * @param del       删除标志
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEsEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/9 10:50
     * @version v1.0
     * @since v1.0
     */
    public List<PaasFormDataEntityExt> getWarehouseListByIdIn(String corpid, Collection<Long> houseIdIn, Integer del) throws XbbException {
        if (houseIdIn == null || houseIdIn.isEmpty()) {
            return new ArrayList<>();
        }
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4BaseByIds(corpid, del, houseIdIn, IndexTypeEnum.IDX_SAAS_WAREHOUSE);
        //需要获取的字段
        String namData = "data." + WarehouseEnum.WAREHOUSE_NAME.getAttr();
        List<String> fieldList = Arrays.asList(FieldTypeEnum.DATAID.getAlias(), namData);
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_WAREHOUSE, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    //-------------------------------获取采购相关-------------------------------
    /**
     * 获取采购产品list
     * @param corpid 公司id
     * @param purchaseIdIn 采购合同id
     * @return java.util.List<com.xbongbong.saas.domain.entity.PurchaseProductEntity>
     * @author zcp
     * @date 2019/4/9 19:38
     * @since v1.0
     * @version v1.0
     */
    public List<PurchaseProductEntity> getPurchaseProductList(String corpid, Collection<Long> purchaseIdIn){
        if (purchaseIdIn == null || purchaseIdIn.size() == 0) {
            return new ArrayList<>();
        }
        Map<String, Object> param = new HashMap<>(3);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("purchaseIdIn", purchaseIdIn);
        List<PurchaseProductEntity> purchaseProductList = purchaseProductModel.findEntitys(param);
        return purchaseProductList;
    }


    /**
     * 获取BOM清单关联产品，关联获取产品的其他参数（如图片，条形码）
     * @param bomIds    关联的bom单id
     * @param corpid    公司id
     * @param type      产品类型 1：成品，2：物料产品
     * @author chaoxiong.lei
     * @Date 11:21 2018/8/13
     * @since
     * @return BOM清单关联产品的list
     */
    public List<BomProductEntity> getBomProducts(List<Long> bomIds, String corpid, Integer type, PageHelper pageHelper) throws XbbException{
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        if (type != null) {
            param.put("type", type);
        }
        param.put("bomIdIn", bomIds);
        List<BomProductEntity> bomProductList = (List<BomProductEntity>) PageHelperUtil.getEntityList(param, pageHelper, bomProductModel);

        List<Long> productIds = new ArrayList<>();
        for (BomProductEntity bomProduct : bomProductList) {
            productIds.add(bomProduct.getProductId());
        }

        String imgsData = ProductEnum.getAttrConnectData(ProductEnum.PRODUCT_IMGS);
        String cidData = ProductEnum.getAttrConnectData(ProductEnum.CATEGORY_ID);
        String barcodeData = ProductEnum.getAttrConnectData(ProductEnum.BARCODE);
        String parentIdData = ProductEnum.getAttrConnectData(ProductEnum.PARENT_ID);
        //需要获取的字段
        List<String> fieldList = Arrays.asList(imgsData, cidData, barcodeData, parentIdData);
        Map<Long, PaasFormDataEntityExt> productMap = getProductMapByIdIn(corpid, productIds, null, fieldList);

        for (BomProductEntity bomProduct : bomProductList) {
            PaasFormDataEntityExt product = productMap.get(bomProduct.getProductId());
            if (product != null) {
                JSONObject productData = product.getData();
                String imageUrl = productData.getString(ProductEnum.PRODUCT_IMGS.getAttr());
                imageUrl = imageUrl == null ? "" : imageUrl;
                Long categpryId = productData.getLong(ProductEnum.CATEGORY_ID.getAttr());
                categpryId = categpryId == null ? 0 : categpryId;
                String barcode = productData.getString(ProductEnum.BARCODE.getAttr());
                barcode = barcode == null ? "" : barcode;
                Long parentId = productData.getLong(ProductEnum.PARENT_ID.getAttr());
                parentId = parentId == null ? 0 : parentId;
                bomProduct.setImageUrl(imageUrl);
                bomProduct.setCategoryId(categpryId);
                bomProduct.setBarcode(barcode);
                bomProduct.setParentProductId(parentId);
                bomProduct.setImageUrlArray(productData.getJSONArray(ProductEnum.PRODUCT_IMGS.getAttr()));
            }
        }
        return bomProductList;
    }

    /**
     * 获取生产单关联产品，关联获取产品的其他参数（如图片，条形码，成本）
     *
     * @param proOrderIds    关联的生产单id
     * @param corpid         公司id
     * @param type           产品类型 1：成品，2：物料产品
     * @author chaoxiong.lei
     * @Date 11:21 2018/8/20
     * @since
     * @return 生产单关联产品的list
     */
    public List<ProductionOrderProductEntity> getProOrderProducts(List<Long> proOrderIds, String corpid, Integer type, PageHelper pageHelper) throws XbbException{
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        if (type != null) {
            param.put("type", type);
        }
        param.put("proOrderIdIn", proOrderIds);
        List<ProductionOrderProductEntity> productList = (List<ProductionOrderProductEntity>) PageHelperUtil.getEntityList(param, pageHelper, productionOrderProductModel);

        List<Long> productIds = new ArrayList<>();
        for (ProductionOrderProductEntity productionOrderProductEntity : productList) {
            productIds.add(productionOrderProductEntity.getProductId());
        }

        String imgsData = ProductEnum.getAttrConnectData(ProductEnum.PRODUCT_IMGS);
        String cidData = ProductEnum.getAttrConnectData(ProductEnum.CATEGORY_ID);
        String barcodeData = ProductEnum.getAttrConnectData(ProductEnum.BARCODE);
        String parentIdData = ProductEnum.getAttrConnectData(ProductEnum.PARENT_ID);
        //需要获取的字段
        List<String> fieldList = Arrays.asList(imgsData, cidData, barcodeData, parentIdData);
        Map<Long, PaasFormDataEntityExt> productMap = getProductMapByIdIn(corpid, productIds, null, fieldList);

        for (ProductionOrderProductEntity productionOrder : productList) {
            PaasFormDataEntityExt product = productMap.get(productionOrder.getProductId());
            if (product != null) {
                JSONObject productData = product.getData();
                String imageUrl = productData.getString(ProductEnum.PRODUCT_IMGS.getAttr());
                imageUrl = imageUrl == null ? "" : imageUrl;
                Long categpryId = productData.getLong(ProductEnum.CATEGORY_ID.getAttr());
                categpryId = categpryId == null ? 0 : categpryId;
                String barcode = productData.getString(ProductEnum.BARCODE.getAttr());
                barcode = barcode == null ? "" : barcode;
                Long parentId = productData.getLong(ProductEnum.PARENT_ID.getAttr());
                parentId = parentId == null ? 0 : parentId;
                productionOrder.setImageUrl(imageUrl);
                productionOrder.setCategoryId(categpryId);
                productionOrder.setBarcode(barcode);
                productionOrder.setParentProductId(parentId);
                productionOrder.setImageUrlArray(productData.getJSONArray(ProductEnum.PRODUCT_IMGS.getAttr()));
            }
        }
        return productList;
    }

    public Map<Long, PaasFormDataEntityExt> getProductMapByIdIn(String corpid, List<Long> productIds, Integer del, List<String> fieldList) throws XbbException{
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        //先获取子产品
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        if (del != null) {
            boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        }
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), productIds));
        List<PaasFormDataEntityExt> childProductList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT,boolQueryBuilder,PaasFormDataEntityExt.class, fieldList);
        childProductList.forEach(product -> {
            productMap.put(product.getId(), product);
        });
        return productMap;
    }

    /**
     * 封装关联id和name到json数组
     *
     * @param dataId
     * @param name
     * @return
     */
    public JSONArray packegeLinkBusinessJsonArray(Long dataId, String name, Integer businessType, Integer saasMark) {
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(StringConstant.SAAS_LINK_BUSINESS_ID, dataId);
        jsonObject.put(StringConstant.SAAS_LINK_BUSINESS_NAME, name);
        jsonObject.put(StringConstant.BUSINESS_TYPE, businessType);
        jsonObject.put(StringConstant.SAAS_MARK, saasMark);
        jsonArray.add(jsonObject);
        return jsonArray;
    }

    /**
     * 新建编辑出入库单获取关联产品是否开启序列号管理
     *
     * @param corpid 公司id
     * @param productArray 产品数据
     * @param handlerExplainInLinkItemDTO 入参
     * @throws XbbException 执行异常
     */
    public void enableSrialCheck(String corpid, JSONArray productArray, HandlerExplainInLinkItemDTO handlerExplainInLinkItemDTO) throws XbbException {
        Integer linkBusinessType = handlerExplainInLinkItemDTO.getLinkBusinessType();
        Integer businessType = handlerExplainInLinkItemDTO.getBusinessType();
        List<Long> productIds = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> parentIds = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject linkProductData = productArray.getJSONObject(i);
            JSONObject jsonObject = linkProductData.getJSONObject(SelectProductEnum.PRODUCT.getAttr());
            productIds.add(jsonObject.getLongValue(BasicConstant.DATAID));
        }
        // 子产品
        List<ProductEntityExt> productEntityExts = productModel.getProductListByIdIn(corpid, productIds, DelEnum.NORMAL.getDel());
        Map<Long, ProductEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ProductEntityExt productEntityExt : productEntityExts) {
            JSONObject entityExtData = productEntityExt.getData();
            long parentId = entityExtData.getLongValue(ProductEnum.PARENT_ID.getAttr());
            parentIds.add(parentId);
            productMap.put(productEntityExt.getId(), productEntityExt);
        }
        // 父产品
        List<ProductEntityExt> parentProductList = productModel.getProductListByIdIn(corpid, parentIds, DelEnum.NORMAL.getDel());
        Map<Long, ProductEntityExt> parentProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ProductEntityExt productEntityExt : parentProductList) {
            parentProductMap.put(productEntityExt.getId(), productEntityExt);
        }
        if (parentIds.size() > 0) {
            for (int i = 0; i < productArray.size(); i++) {
                JSONObject linkProductData = productArray.getJSONObject(i);
                JSONObject jsonObject = linkProductData.getJSONObject(SelectProductEnum.PRODUCT.getAttr());
                ProductEntityExt productEntityExt = productMap.get(jsonObject.getLongValue(BasicConstant.DATAID));
                if (productEntityExt == null) {
                    continue;
                }
                JSONObject productData = productEntityExt.getData();
                JSONObject sourceData = jsonObject.getJSONObject("sourceData");
                long parentId = productData.getLongValue(ProductEnum.PARENT_ID.getAttr());
                ProductEntityExt parentProduct = parentProductMap.get(parentId);
                if (parentProduct == null || Objects.isNull(parentProduct.getData())) {
                    continue;
                }
                JSONObject parentProductData = parentProduct.getData();

                linkProductData.put(ProductEnum.ENABLE_SERIAL_NUMBER.getSaasAttr(), parentProductData.getLongValue(ProductEnum.ENABLE_SERIAL_NUMBER.getAttr()));
                if (BasicConstant.ONE.equals(parentProductData.getInteger(ProductEnum.ENABLE_SERIAL_NUMBER.getAttr()))) {
                    linkProductData.put(SelectProductEnum.NUM.getAttr(), null);
                }
                Integer enableBatch = parentProductData.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr()) == null ? 0 : parentProductData.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr());
                linkProductData.put(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr(), enableBatch);
                if (Objects.equals(enableBatch, 0)) {
                    linkProductData.remove(SelectProductEnum.BATCH.getAttr());
                    linkProductData.remove(SelectProductEnum.GUARANTEE_PERIOD.getAttr());
                    linkProductData.remove(SelectProductEnum.PRODUCE_DATE.getAttr());
                    linkProductData.remove(SelectProductEnum.EXPIRE_DATE.getAttr());
                    linkProductData.put(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr(), 0);
                }else {
                    if (StringUtil.isEmpty(parentProductData.getString(ProductEnum.SHELF_LIFE_DAYS.getAttr()))) {
                        linkProductData.put(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr(), 0);
                        linkProductData.remove(SelectProductEnum.GUARANTEE_PERIOD.getAttr());
                        linkProductData.remove(SelectProductEnum.PRODUCE_DATE.getAttr());
                        linkProductData.remove(SelectProductEnum.EXPIRE_DATE.getAttr());
                    }else {
                        linkProductData.put(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr(), 1);
                        if (Objects.isNull(linkProductData.get(SelectProductEnum.GUARANTEE_PERIOD.getAttr()))) {
                            linkProductData.put(SelectProductEnum.GUARANTEE_PERIOD.getAttr(), parentProductData.get(ProductEnum.SHELF_LIFE_DAYS.getAttr()));
                            linkProductData.put(SelectProductEnum.PRODUCE_DATE.getAttr(), DateTimeUtil.getTodayInt());
                            linkProductData.put(SelectProductEnum.EXPIRE_DATE.getAttr(), DateTimeUtil.getTodayInt() + parentProductData.getLong(ProductEnum.SHELF_LIFE_DAYS.getAttr()) * TimeConstant.SECONDS_PER_DAY);
                        }
                    }
                }
                if (Objects.nonNull(sourceData)) {
                    sourceData.put(ProductEnum.ENABLE_SERIAL_NUMBER.getSaasAttr(), linkProductData.get(ProductEnum.ENABLE_SERIAL_NUMBER.getSaasAttr()));
                    sourceData.put(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr(), linkProductData.get(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr()));
                    sourceData.put(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr(), linkProductData.get(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr()));
                    sourceData.put(ProductConstant.UNIT_RATE, linkProductData.get(ProductConstant.UNIT_RATE));
                    sourceData.put(ProductConstant.TRANSFORM_UNIT_RATE, linkProductData.get(ProductConstant.TRANSFORM_UNIT_RATE));
                    sourceData.put(ProductEnum.ENABLE_MULTI_UNIT.getSaasAttr(), linkProductData.get(ProductEnum.ENABLE_MULTI_UNIT.getSaasAttr()));
                    sourceData.put(ProductConstant.PRICE, linkProductData.get(ProductConstant.PRICE));
                }
            }
        }
    }

    public JSONArray getMultiUnit(String corpid, String groupId, Integer accuracy) throws XbbException {
        ProductFieldPojo productFieldPojo = saasFormHelp.getProductFieldPojo(RedisPrefixConstant.PRODUCT_UNIT, ProductEnum.UNIT.getAttr(), corpid);
        List<MultiUnitItemPoJo> multiItems = productFieldPojo.getMultiItems();

        JSONArray unitArray = new JSONArray();
        for (MultiUnitItemPoJo multiItem : multiItems) {
            if (!Objects.equals(Long.parseLong(groupId), multiItem.getValue())) {
                continue;
            }
            List<UnitItemPoJo> itemDataPoJoList = multiItem.getItemDataPoJoList();
            for (UnitItemPoJo unitItemPoJo : itemDataPoJoList) {
                JSONObject unitJSON = new JSONObject();
                unitJSON.put(MultiUnitConstant.VALUE, unitItemPoJo.getValue());
                unitJSON.put(MultiUnitConstant.MULTI_UNIT_TEXT, unitItemPoJo.getText());
                unitJSON.put(MultiUnitConstant.RATE, unitItemPoJo.getRate());
                unitJSON.put(MultiUnitConstant.IS_BASE, unitItemPoJo.getIsBase());
                unitJSON.put(MultiUnitConstant.ACCURACY, accuracy);

                unitArray.add(unitJSON);
            }
        }
        return unitArray;
    }


    /**
     * 序列号保存前置校验，避免重复序列号，主要是避免审批导致的序列号重复
     *
     * @param formData     表单数据
     * @param productArray 关联产品数据
     * @param productAttr  关联产品attr
     * @param businessType 业务类型
     * @param corpid       公司id
     * @throws XbbException 校验不通过
     */
    public void preCheckSaveSerial(JSONObject formData, JSONArray productArray, String productAttr, Integer businessType, String corpid) throws XbbException {
        List<String> seqKeyList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String, JSONObject> seqKeyAndWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> productIdSet = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> productIdList = new ArrayList<>();
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject productData = productArray.getJSONObject(i);
            if (BasicConstant.ZERO.equals(productData.getIntValue(ProductEnum.ENABLE_SERIAL_NUMBER.getSaasAttr())) || !productData.containsKey(ProductConstant.SEQ)) {
                continue;
            }
            JSONArray seqArray = productData.getJSONArray(ProductConstant.SEQ);
            Long productId = productData.getLong(SelectProductEnum.PRODUCT.getAttr());
            for (int j = 0; j < seqArray.size(); j++) {
                JSONObject seqData = seqArray.getJSONObject(j);
                String seq = seqData.getString(SerialEnum.SERIAL.getAttr());
                seqKeyList.add(productId + "_" + seq);

                seqKeyAndWarehouseMap.put(productId + "_" + seq, productData);
            }
            productIdSet.add(productId);
            productIdList.add(productId);
        }

        // 新建单据没有序列号数据时，不做校验
        if (CollectionUtils.isEmpty(seqKeyList)) {
            return;
        }

        // 产品信息
        List<PaasFormDataEntityExt> productEntityList = productModel.getProductListById(productIdSet, corpid, DelEnum.NORMAL);
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(productEntityList.size());
        productEntityList.forEach(item -> productMap.put(item.getDataId(), item));

        // 序列号的当前结存状态
        List<ProductSerialBalanceEntity> serialBalanceEntities = productSerialBalanceModel.getBySeqKeyList(seqKeyList, corpid);
        List<String> seqList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String, ProductSerialBalanceEntity> seqKeyAndRefIdMap = new HashMap<>();
        serialBalanceEntities.forEach(item -> seqList.add(item.getSeq()));
        serialBalanceEntities.forEach(item -> seqKeyAndRefIdMap.put(item.getSeqKey(), item));

        LinkProductSelectSerialEnum linkProductSelectSerialEnum;
        XbbRefTypeEnum refTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        switch (refTypeEnum) {
            // 期初入库、采购入库、生产入库，审批通过时判断录入序列号是否被其他单据入库
            case ORI_STOCK:
            case PURCHASE_INSTOCK:
            case PRODUCTION_INSTOCK:
                if (CollectionUtils.isNotEmpty(serialBalanceEntities)) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205071, String.format(ProductErrorCodeEnum.API_ERROR_205071.getMsg(), seqList.toString()));
                }
                break;
            case RETURNED_PURCHASE:
                // 如果关联产品选择的序列号已不在系统中，则审批不通过
                if (CollectionUtils.isEmpty(serialBalanceEntities)) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205088);
                }
                for (int i = 0; i < productArray.size(); i++) {
                    JSONObject productData = productArray.getJSONObject(i);
                    // 未开启序列号管理的产品不做校验
                    if (BasicConstant.ZERO.equals(productData.getIntValue(ProductEnum.ENABLE_SERIAL_NUMBER.getSaasAttr())) || !productData.containsKey(ProductConstant.SEQ)) {
                        continue;
                    }
                    Integer warehosueId = productData.getJSONArray(InstockProductEnum.WAREHOUSE.getAttr()).getJSONObject(0).getInteger("id");
                    JSONArray serialArray = productData.getJSONArray(ProductConstant.SEQ);
                    List<String> serialList = new ArrayList<>();
                    for (int j=0; j<serialArray.size(); j++){
                        String seq = serialArray.getJSONObject(j).getString(InstockProductEnum.PRODUCT.getAttr());
                        serialList.add(seq);
                    }
                    Long productId = productData.getLong(SelectProductEnum.PRODUCT.getAttr());
                    Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    param.put("corpid", corpid);
                    param.put("productId", productId);
                    param.put("warehouseId", warehosueId);
                    param.put("seqIn", serialList);
                    param.put("status", SerialStatusEnum.IN_STOCK.getCode());
                    param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                    List<ProductSerialBalanceEntity> productSerialBalanceEntityList = productSerialBalanceModel.findEntitys(param);
                    Map<String, ProductSerialBalanceEntity> seqMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    productSerialBalanceEntityList.forEach(item -> {
                        seqMap.put(item.getSeqKey(), item);
                    });
                    // 判断当前单据审批通过时，序列号结存状态是否还是该单据的上游单据中
                    PaasFormDataEntityExt paasFormDataEntityExt = productMap.get(productId);
                    JSONObject extData = paasFormDataEntityExt.getData();
                    for (int j = 0; j < serialArray.size(); j++) {
                        JSONObject seqData = serialArray.getJSONObject(j);
                        String seq = seqData.getString(SerialEnum.SERIAL.getAttr());
                        String seqKey = productId + "_" + seq;
                        Long refId = seqMap.get(seqKey).getRefId();
                        Integer linkBusinessType = seqMap.get(seqKey).getBusinessType();
                        ProductSerialBalanceEntity serialBalanceEntity = seqKeyAndRefIdMap.getOrDefault(seqKey, null);
                        String refSheetNo = serialBalanceEntity.getRefSheetNo();
                        if (!InstockTypeEnum.getByStockBuisenssType(linkBusinessType) && Objects.equals(serialBalanceEntity.getRefId(), refId)) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205085, String.format(ProductErrorCodeEnum.API_ERROR_205085.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq, refTypeEnum.getName() + "[" + refSheetNo + "]"));
                        } else if (Objects.isNull(serialBalanceEntity)) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205087);
                        }
                    }

                }
                break;
            case REFUND:
            case RETURNED_MATERIEL_INSTOCK:
                // 如果关联产品选择的序列号已不在系统中，则审批不通过
                if (CollectionUtils.isEmpty(serialBalanceEntities)) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205088);
                }
                linkProductSelectSerialEnum = LinkProductSelectSerialEnum.getByBusinessType(businessType);
                Integer linkBusinessType = linkProductSelectSerialEnum.getLinkBusinessType();
                for (int i = 0; i < productArray.size(); i++) {
                    JSONObject productData = productArray.getJSONObject(i);
                    // 未开启序列号管理的产品不做校验
                    if (BasicConstant.ZERO.equals(productData.getIntValue(ProductEnum.ENABLE_SERIAL_NUMBER.getSaasAttr())) || !productData.containsKey(ProductConstant.SEQ)) {
                        continue;
                    }
                    long refProductId = productData.getLongValue(BusinessConstant.REF_PRODUCT_ID);
                    // 取退货产品的上游出库单ID,退货产品可以是多个销售出库单的产品
                    OutstockProductEntity outstockProductEntity = outstockProductModel.getByKey(refProductId, corpid);
                    Long refId = outstockProductEntity.getOutWarehouseId();
                    // 判断当前单据审批通过时，序列号结存状态是否还是该单据的上游单据中
                    JSONArray seqArray = productData.getJSONArray(ProductConstant.SEQ);
                    Long productId = productData.getLong(SelectProductEnum.PRODUCT.getAttr());
                    PaasFormDataEntityExt paasFormDataEntityExt = productMap.get(productId);
                    JSONObject extData = paasFormDataEntityExt.getData();
                    for (int j = 0; j < seqArray.size(); j++) {
                        JSONObject seqData = seqArray.getJSONObject(j);
                        String seq = seqData.getString(SerialEnum.SERIAL.getAttr());
                        String seqKey = productId + "_" + seq;
                        ProductSerialBalanceEntity serialBalanceEntity = seqKeyAndRefIdMap.getOrDefault(seqKey, null);
                        String refSheetNo = serialBalanceEntity.getRefSheetNo();
                        if (!(Objects.equals(linkBusinessType, serialBalanceEntity.getBusinessType()) && Objects.equals(serialBalanceEntity.getRefId(), refId))) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205085, String.format(ProductErrorCodeEnum.API_ERROR_205085.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq, refTypeEnum.getName() + "[" + refSheetNo + "]"));
                        } else if (Objects.isNull(serialBalanceEntity)) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205087);
                        }
                    }

                }
                break;
            case RETURNED_PURCHASE_OUTSTOCK:
            case REFUND_INSTOCK:
                // 如果关联产品选择的序列号已不在系统中，则审批不通过
                if (CollectionUtils.isEmpty(serialBalanceEntities)) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205088);
                }

                // 判断当前单据在待审批时，序列号是否已被其他相同业务类型单据所选择
                Long refId;
                if (Objects.equals(refTypeEnum, XbbRefTypeEnum.REFUND_INSTOCK)) {
                    JSONArray jsonArray = formData.getJSONArray(InstockEnum.REF_ID.getAttr());
                    refId = jsonArray.getJSONObject(0).getLong("id");
                } else {
                    JSONArray jsonArray = formData.getJSONArray(OutstockEnum.REF_ID.getAttr());
                    refId = jsonArray.getJSONObject(0).getLong("id");
                }
                linkProductSelectSerialEnum = LinkProductSelectSerialEnum.getByBusinessType(businessType);
                linkBusinessType = linkProductSelectSerialEnum.getLinkBusinessType();
                // 判断当前单据审批通过时，序列号结存状态是否还是该单据的上游单据中
                for (ProductSerialBalanceEntity serialBalanceEntity : serialBalanceEntities) {
                    String seq = serialBalanceEntity.getSeq();
                    String refSheetNo = serialBalanceEntity.getRefSheetNo();
                    PaasFormDataEntityExt paasFormDataEntityExt = productMap.get(serialBalanceEntity.getProductId());
                    JSONObject extData = paasFormDataEntityExt.getData();
                    if (!(Objects.equals(linkBusinessType, serialBalanceEntity.getBusinessType()) && Objects.equals(serialBalanceEntity.getRefId(), refId))) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205085, String.format(ProductErrorCodeEnum.API_ERROR_205085.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq, refTypeEnum.getName() + "[" + refSheetNo + "]"));
                    }
                }
                break;
            case CONTRACT_OUTSTOCK:
            case PRODUCTION_MATERIEL_OUTSTOCK:
            case WORK_ORDER_OUTSTOCK:
            case OTHER_OUTSTOCK:
            case TRANSFER:
                // 如果关联产品选择的序列号已不在库中，则审批不通过
                if (CollectionUtils.isEmpty(serialBalanceEntities)) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205088);
                }

                // 如果关联产品选择的序列号已不再当前仓库中，则审批不通过
                checkTaskCommit(formData, serialBalanceEntities, productMap, seqKeyAndWarehouseMap, refTypeEnum, corpid);
                break;
            case OTHER_INSTOCK:
                Integer instockType = formData.getInteger(InstockEnum.TYPE.getAttr());
                if (Objects.equals(InstockTypeEnum.OTHER_INSTOCK.getCode(), instockType) || Objects.equals(InstockTypeEnum.INVENTORY_INSTOCK.getCode(), instockType) || Objects.equals(InstockTypeEnum.ASSEMBLE_INSTOCK.getCode(), instockType)) {
                    // 其他入库、盘盈入库审、装配入库批通过时判断序列号是否已经被入库
                    if (CollectionUtils.isNotEmpty(serialBalanceEntities)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205071, String.format(ProductErrorCodeEnum.API_ERROR_205071.getMsg(), seqList.toString()));
                    }
                } else if (Objects.equals(InstockTypeEnum.TRANSFER_INSTOCK.getCode(), instockType)) {
                    for (int i = 0; i < productArray.size(); i++) {
                        JSONObject productData = productArray.getJSONObject(i);
                        long refProductId = productData.getLongValue(BusinessConstant.REF_PRODUCT_ID);
                        // 取调拨入库产品的上游调拨出库单ID
                        OutstockProductEntity outstockProductEntity = outstockProductModel.getByKey(refProductId, corpid);
                        refId = outstockProductEntity.getOutWarehouseId();

                        linkBusinessType = XbbRefTypeEnum.OTHER_OUTSTOCK.getCode();
                        // 判断当前单据审批通过时，序列号结存状态是否还是该单据的上游单据中
                        for (ProductSerialBalanceEntity serialBalanceEntity : serialBalanceEntities) {
                            String seq = serialBalanceEntity.getSeq();
                            String refSheetNo = serialBalanceEntity.getRefSheetNo();
                            PaasFormDataEntityExt paasFormDataEntityExt = productMap.get(serialBalanceEntity.getProductId());
                            JSONObject extData = paasFormDataEntityExt.getData();
                            if (!(Objects.equals(linkBusinessType, serialBalanceEntity.getBusinessType()) && Objects.equals(serialBalanceEntity.getRefId(), refId))) {
                                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205085, String.format(ProductErrorCodeEnum.API_ERROR_205085.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq, refTypeEnum.getName() + "[" + refSheetNo + "]"));
                            }
                        }
                    }
                }
                break;
            case ASSEMBLE:
                if (AssembleEnum.IN_PRODUCTS.getAttr().equals(productAttr)) {
                    // 装配入库审批通过时判断序列号是否已经被入库
                    if (CollectionUtils.isNotEmpty(serialBalanceEntities)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205071, String.format(ProductErrorCodeEnum.API_ERROR_205071.getMsg(), seqList.toString()));
                    }
                } else if (AssembleEnum.OUT_PRODUCTS.getAttr().equals(productAttr)) {
                    // 如果关联产品选择的序列号已不在库中，则审批不通过
                    if (CollectionUtils.isEmpty(serialBalanceEntities)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205088);
                    }

                    checkTaskCommit(formData, serialBalanceEntities, productMap, seqKeyAndWarehouseMap, refTypeEnum, corpid);
                }
                break;
            default:
                break;
        }
    }

    private void checkTaskCommit(JSONObject formData, List<ProductSerialBalanceEntity> serialBalanceEntities, Map<Long, PaasFormDataEntityExt> productMap, Map<String, JSONObject> seqKeyAndWarehouseMap, XbbRefTypeEnum refTypeEnum, String corpid) throws XbbException {
        // 如果关联产品选择的序列号已不再当前仓库中，则审批不通过
        for (ProductSerialBalanceEntity serialBalanceEntity : serialBalanceEntities) {
            String seqKey = serialBalanceEntity.getSeqKey();
            String seq = serialBalanceEntity.getSeq();
            JSONObject productData = seqKeyAndWarehouseMap.getOrDefault(seqKey, null);
            PaasFormDataEntityExt paasFormDataEntityExt = productMap.get(serialBalanceEntity.getProductId());
            JSONObject extData = paasFormDataEntityExt.getData();

            if (Objects.nonNull(productData)) {
                // 当前单据出库仓库id
                Long warehouseId;
                if (Objects.equals(refTypeEnum, XbbRefTypeEnum.TRANSFER)) {
                    JSONArray warehouseArray = formData.getJSONArray(TransferEnum.OUT_WAREHOUSE_ID.getAttr());
                    warehouseId = warehouseArray.getJSONObject(0).getLong("id");
                } else {
                    JSONArray warehouseArray = productData.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr());
                    warehouseId = warehouseArray.getJSONObject(0).getLong("id");
                }
                if (!Objects.equals(warehouseId, serialBalanceEntity.getWarehouseId())) {
                    WarehouseEntityExt warehouseEntityExt = warehouseModel.getByKey(warehouseId, corpid);
                    String warehouseName = warehouseEntityExt.getData().getString(WarehouseEnum.WAREHOUSE_NAME.getAttr());
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205086, String.format(ProductErrorCodeEnum.API_ERROR_205086.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq, warehouseName));
                } else if (Objects.equals(SerialStatusEnum.OUT_STOCK.getCode(), serialBalanceEntity.getStatus())) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205089, String.format(ProductErrorCodeEnum.API_ERROR_205089.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq));
                } else if (Objects.equals(refTypeEnum.getCode(), serialBalanceEntity.getBusinessType())) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205092, String.format(ProductErrorCodeEnum.API_ERROR_205092.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), seq, serialBalanceEntity.getRefSheetNo()));
                }
            } else {
                // 序列号结存表中没有当前表单所选序列号，则代表序列号已被删除，不可使用，审批不通过
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205087, String.format(ProductErrorCodeEnum.API_ERROR_205087.getMsg(), extData.getString(ProductEnum.NAME.getAttr()), extData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr())), seq);
            }
        }
    }


    public List<FieldAttrEntity> getProductPriceFieldList(String corpid) throws XbbException {
        List<FieldAttrEntity> fieldAttrList = new ArrayList<>();
        List<String> fieldAttr = Arrays.asList(PriceSetEnum.NAME.getAttr(),PriceSetEnum.PRIORITY.getAttr(),PriceSetEnum.PRODUCT.getAttr(), PriceSetEnum.ADAPTER_TYPE.getAttr());
        PaasFormExplainEntity contractFormExplain = paasFormExplainModel.getByBusinessType(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), "0");
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(contractFormExplain.getExplains());
        FieldAttrEntity contractProductField = explainMap.get(SalesOpportunityEnum.PRODUCTS.getAttr());
        List<? extends FieldAttrEntity> items = contractProductField.getSubForm().getItems();
//        LinkInfoPojo linkInfoPojo = null;
//        for (FieldAttrEntity item : items) {
//            if (OpportunityProductEnum.PRODUCT.getAttr().equals(item.getAttr())) {
//                linkInfoPojo = item.getLinkInfo();
//                break;
//            }
//        }
        PaasFormEntityExt formEntityExt = paasFormModel.getByBusinessType(XbbRefTypeEnum.PRODUCT.getCode(), corpid);
        for (PriceSetEnum priceSetEnum : PriceSetEnum.values()) {
            if (!fieldAttr.contains(priceSetEnum.getAttr())) {
                continue;
            }
            FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
            fieldAttrEntity.setAttr(priceSetEnum.getAttr());
            fieldAttrEntity.setAttrName(priceSetEnum.getAttrName());
            fieldAttrEntity.setSaasAttr(priceSetEnum.getSaasAttr());
            fieldAttrEntity.setFieldType(priceSetEnum.getFieldType());
            fieldAttrEntity.setSort(priceSetEnum.getSort());
            fieldAttrEntity.setVisible(priceSetEnum.getVisible());
            fieldAttrEntity.setRequired(priceSetEnum.getRequired());
            fieldAttrEntity.setEditable(priceSetEnum.getEditable());
            fieldAttrEntity.setSaasMark(SaasMarkEnum.SAAS.getCode());
            if (Objects.equals(priceSetEnum.getAttr(), PriceSetEnum.NAME.getAttr())) {
                fieldAttrEntity.setFieldType(FieldTypeEnum.TEXT.getType());
            } else if (Objects.equals(priceSetEnum.getAttr(), PriceSetEnum.PRODUCT.getAttr())) {
                fieldAttrEntity.setFieldType(FieldTypeEnum.SELECT_PRODUCT.getType());
                SubFormPoJo subFormPoJo = new SubFormPoJo();
                FieldAttrEntity field = items.get(0);
                LinkInfoPojo linkInfo = new LinkInfoPojo();
                linkInfo.setLinkAppId(formEntityExt.getAppId());
                linkInfo.setLinkFormId(formEntityExt.getId());
                linkInfo.setLinkMenuId(formEntityExt.getMenuId());
                linkInfo.setLinkSaasMark(formEntityExt.getSaasMark());
                linkInfo.setLinkBusinessType(XbbRefTypeEnum.PRODUCT.getCode());
                field.setLinkInfo(linkInfo);
                subFormPoJo.setItems(Collections.singletonList(field));
                fieldAttrEntity.setSubForm(subFormPoJo);
            }
            fieldAttrEntity.setDisableListSort(1);
            fieldAttrList.add(fieldAttrEntity);
        }

        for (PriceProductEnum priceProductEnum : PriceProductEnum.values()) {
            FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
            fieldAttrEntity.setAttr(priceProductEnum.getAttr());
            fieldAttrEntity.setAttrName(priceProductEnum.getAttrName());
            fieldAttrEntity.setSaasAttr(priceProductEnum.getSaasAttr());
            fieldAttrEntity.setFieldType(priceProductEnum.getFieldType());
            fieldAttrEntity.setSort(priceProductEnum.getSort());
            fieldAttrEntity.setVisible(priceProductEnum.getVisible());
            fieldAttrEntity.setRequired(priceProductEnum.getRequired());
            fieldAttrEntity.setEditable(priceProductEnum.getEditable());
            fieldAttrEntity.setSaasMark(SaasMarkEnum.SAAS.getCode());
            fieldAttrEntity.setDisableListSort(1);
            if (Objects.equals(priceProductEnum.getAttr(), PriceProductEnum.NAME.getAttr())) {
                fieldAttrEntity.setFieldType(FieldTypeEnum.TEXT.getType());
            }

            fieldAttrList.add(fieldAttrEntity);
        }
        return fieldAttrList;
    }

    /**
     * 获取活动产品
     * @param corpid
     * @param distributorId
     */
    public Map<Long, PromotionProductEntity> getActivityList(String corpid, Long distributorId, List<Long> productIdIn) throws XbbException {
        // 查询是否有该经销商适用的活动
        List<PaasFormDataEntityExt> activityList = queryOnlineActivity(corpid, distributorId);
        List<Long> activityIdList = new ArrayList<>();
        activityList.forEach(item -> activityIdList.add(item.getDataId()));
        List<PromotionProductEntity> promotionList= new ArrayList<>();
        if (!activityIdList.isEmpty()) {
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put(ParameterConstant.CORPID, corpid);
            params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            params.put("productIdIn", productIdIn);
            params.put("refIdIn", activityIdList);
            promotionList = promotionProductModel.findEntities(params);
        }
        Map<Long, PromotionProductEntity> promotionMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        promotionList.forEach(item->{
            if (!promotionMap.containsKey(item.getProductId()) || promotionMap.get(item.getProductId()).getPriority() > item.getPriority()) {
                promotionMap.put(item.getProductId(), item);
            }
        });
        return promotionMap;
    }

    /**
     * 查询是否有该经销商适用的活动
     * @param
     * @return
     */
    public List<PaasFormDataEntityExt>  queryOnlineActivity(String corpid, Long distributorId) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        packageBaseCondition(corpid, boolQueryBuilder);
        boolQueryBuilder.filter(rangeQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.END_TIME)).gte(DateTimeUtil.getTodayInt()));
        boolQueryBuilder.filter(rangeQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.BEGIN_TIME)).lte(DateTimeUtil.getTodayInt()));
        packageDistributorCondition(corpid, distributorId, boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PROMOTION_ACTIVITY_MANAGEMENT.getIndex());
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.ASC));
        List<String> fieldList = Arrays.asList(FieldTypeEnum.DATAID.getAlias(), PromotionActivityManagementEnum.PRIORITY.getAttr());
        EsUtil.setFieldList(sourceBuilder, fieldList);
        searchRequest.source(sourceBuilder);
        PageRequest pageRequest= EsUtil.setPage(sourceBuilder, 1, 50);
        XbbAggregatedPage<PaasFormDataEntityExt> xbbAggregatedPage = xbbElasticsearchRestTemplate.queryForPagesWithoutCount(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        List<PaasFormDataEntityExt> content = xbbAggregatedPage.getContent();
        return content;
    }

    public void packageBaseCondition(String corpid, BoolQueryBuilder boolQueryBuilder){
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.STATUS), MarketingStatusEnum.ONLINE.getCode()));
    }

    public void packageDistributorCondition(String corpid, Long distributorId, BoolQueryBuilder boolQueryBuilder){
        CustomerEntityExt customerEntityExt = customerModel.getByKey(distributorId, corpid);
        //拿到经销商级别
        Object level = customerEntityExt.getData().get(DistributorEnum.SCALE.getAttr());
        Long area = customerEntityExt.getData().getLongValue(DistributorEnum.AREA.getAttr());
        BoolQueryBuilder allAreaShouldBuilder = boolQuery().mustNot(existsQuery(MarketingBaseEnum.APPLICABLE_LOCATION.getAttr()));
        BoolQueryBuilder specificAreaShouldBuilder = boolQuery().filter(termsQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.APPLICABLE_LOCATION), Arrays.asList(area)));
        //解析查询条件
        BoolQueryBuilder shouldBuilder = boolQuery();
        shouldBuilder.filter(termQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.APPLICABLE_OBJECT_TYPE), ApplicableObjectTypeEnum.ALL.getCode()));
        shouldBuilder.should(specificAreaShouldBuilder).should(allAreaShouldBuilder).minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
        boolQueryBuilder.should(shouldBuilder);
        shouldBuilder = boolQuery();
        shouldBuilder.filter(termQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.APPLICABLE_OBJECT_TYPE), ApplicableObjectTypeEnum.DISTRIBUTOR_LEVEL.getCode()));
        if (Objects.nonNull(level)) {
            shouldBuilder.filter(termsQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.DISTRIBUTOR_LEVEL), Arrays.asList(level)));
        }
        boolQueryBuilder.should(shouldBuilder);
        shouldBuilder.should(specificAreaShouldBuilder).should(allAreaShouldBuilder).minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
        shouldBuilder = boolQuery();
        shouldBuilder.filter(termQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.APPLICABLE_OBJECT_TYPE), ApplicableObjectTypeEnum.SPECIFIC_DISTRIBUTOR.getCode()));
        shouldBuilder.filter(termsQuery(MarketingBaseEnum.getAttrConnectData(MarketingBaseEnum.DISTRIBUTOR),  Arrays.asList(distributorId)));
        shouldBuilder.should(specificAreaShouldBuilder).should(allAreaShouldBuilder).minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
        boolQueryBuilder.should(shouldBuilder);
        boolQueryBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
    }

}
